/* tripl/arch/interp/interp.c 
 * 
 * This file is part of tripl. 
 * 
 * tripl is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * tripl is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with tripl. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <dlfcn.h>

#include <errno.h>

#include <klist.h>
#include <tripl/tripl.h>
#include <tripl/limits.h>
#include <tripl/arch.h>


int interp_init(void);

__arch int interp_new_var(char *name, long value, int attr);
__arch int interp_dump(int);
__arch int interp_set_var(char *name, long value, int attr);
__arch int interp_import_shared(char *name);
__arch int interp_math_expr(char *p, char *var);

extern struct klist_node *find_name(char *name);

extern char *psave;

struct klist_node libs = KLIST_NODE_INIT(&libs);
extern struct klist_node names_list;



struct arch interp_arch_fn = {
	.arch_init = interp_init, 
	.arch_new_var = interp_new_var, 
	.arch_dump = interp_dump,  
	.arch_import_shared = interp_import_shared, 
	.arch_funcall = NULL, 
	.arch_set_var = interp_set_var, 
	.arch_math_expr = interp_math_expr
};


__arch int interp_init(void) {
	return 0;
}



__arch int interp_new_var(char *name, long value, int attr) {
	__push_nr(&names_list, value, name, attr);

	return 0;
}

__arch int interp_set_var(char *name, long value, int attr) {
	struct klist_node *n;

	/* find the name */

	if (unlikely(!(n = find_name(name)))) 
		exit(generic_error("name not found", psave, lno));

	if (unlikely(n->xmask & 4)) 
		exit(generic_error("variable sets to be read-only", psave, lno));

	n->value = value;

	return 0;
}


#define NAMES_VARS 	1
#define NAMES_FNS 	2
#define NAMES_ANY 	3

__arch static int interp_names_dump(int names) {
	struct klist_node *n;

	ansi_csi_st(stdout, MODE_HIGHLIGHT, FG_LIGHTBLUE, BG_BLACK);

	if (names == NAMES_VARS) 
		printf("dump variables: \n");

	else if (names == NAMES_FNS) 
		printf("dump functions: \n");

	else 
		printf("names dump: \n");

	ansi_csi_ed(stdout);

	for (n = names_list.next; n != &names_list; n = n->next) {
		if (n->xmask & XMASK_FN && 
				(n->value & DECLARE_FN) && (names & NAMES_FNS)) {
			int nargs = n->value & (DECLARE_FN - 1);

			if (nargs == DECLARE_NOARG) 
				nargs = 0;

			printf("\tfn %s: \t%d arguments\n", (char *) n->payload, nargs);
		}

		
		if (!(n->value & DECLARE_FN) && (names & NAMES_VARS)) {
			/* variables */
			if (n->xmask & XMASK_WEAK)
				printf("\tvar %s\n", (char *) n->payload);

			else 
				printf("\tvar %s: \t\t%ld\n", (char *) n->payload, n->value);
		}
	}

	printf("\n");

	return 0;
}


__arch static int interp_libs_dump(void) {
	struct klist_node *n;

	ansi_csi_st(stdout, MODE_HIGHLIGHT, FG_LIGHTBLUE, BG_BLACK);

	printf("dump libraries: \n");

	ansi_csi_ed(stdout);

	for (n = libs.next; n != &libs; n = n->next) 
		printf("\tlib %s: handle %p\n", (char *) n->data, n->payload);

	printf("\n");

	return 0;
}


__arch int interp_dump(int attr) {
	if (!attr || attr == 16) {
		/* dump all */

		interp_names_dump(NAMES_ANY);
		interp_libs_dump();

		return 0;
	}

	if (attr & 1) 
		interp_names_dump(NAMES_VARS);

	if (attr & 2) 
		interp_names_dump(NAMES_FNS);

	if (attr & 4) 
		interp_names_dump(NAMES_ANY);

	if (attr & 8) 
		interp_libs_dump();

	return 0;
}


__arch int interp_import_shared(char *name) {
	struct klist_node *n;
	void *dl;
	
	if (!(dl = dlopen(name, RTLD_LAZY))) 
		exit(generic_error(dlerror(), psave, lno));

	n = malloc(sizeof(struct klist_node));
	klist_init(n);

	n->payload = dl;
	n->data = name;

	klist_add(&libs, n);

	return 0;
}



